#ifndef PROTOCOL_HPP
#define PROTOCOL_HPP

#include <iostream>
#include <cstdlib>
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>
#include <unordered_map>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "Log.hpp"
#include "Util.hpp"

#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_NOT_FOUND "404.html"
#define PAGE_BAD_REQUEST "400.html"
#define PAGE_SERVER_ERROR "500.html"

#define OK 200
#define NOT_FOUND 404
#define BAD_REQUEST 400
#define SERVER_ERROR 500


static std::string Code2Desc(int code)
{
    switch(code)
    {
        case 200:
            return "OK";
        case 404:
            return "NOT FOUND";
        default:
            return "UNKNOWN";
    }
}

static std::string Suffix2Desc(const std::string& suffix) 
{
    static std::unordered_map<std::string, std::string> suffix_desc = {
        {".html", "text/html"},
        {".jpg", "image/jpeg"},
        {".png", "image/png"},
        {".gif", "image/gif"},
        {".mp3", "audio/mpeg"},
        {".mp4", "video/mp4"},
        {".txt", "text/plain"},
        {".pdf", "application/pdf"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".json", "application/json"},
        {".xml", "application/xml"}
    };

    auto it = suffix_desc.find(suffix);
    if (it != suffix_desc.end()) 
    {
        return it->second;
    }
    return "text/html";
}

class HttpRequest
{
public:
    std::string _request_line;
    std::vector<std::string> _request_header;
    std::string _blank; // 空行
    std::string _request_body;

    // 解析完毕之后的结果
    std::string _method;
    std::string _uri;  
    std::string _version;

    std::unordered_map<std::string, std::string> _header_kv;
    int content_length;
    std::string _path;
    std::string _suffix;
    std::string _query_string;

    bool cgi;
    int _size;
public:
    HttpRequest(): content_length(0), cgi(false), _size(0) {}
    ~HttpRequest() {}
};

class HttpResponse
{
public:
    std::string _status_line;
    std::vector<std::string> _response_header;
    std::string _blank; // 空行
    std::string _response_body;

    int _status_code;
    int _fd;
public:
    HttpResponse() : _blank(LINE_END), _status_code(OK), _fd(-1) {}
    ~HttpResponse() {}
};

// 读取请求，解析请求，构建响应
// IO通信
class Endpoint
{
private:
    int _sock;
    HttpRequest _http_req;
    HttpResponse _http_resp;
    bool stop;
private:
    bool RecvHttpRequestLine()
    {
        auto& line = _http_req._request_line;
        if(Util::ReadLine(_sock, _http_req._request_line) > 0)
        {
            line.resize(line.size()-1);    // 去掉\n
            LOG(INFO, _http_req._request_line);
        }
        else 
        {
            stop = true;
        }
        return stop;
    }

    bool RecvHttpRequestHeader()
    {
        std::string line;
        while(true)
        {
            line.clear();
            if(Util::ReadLine(_sock, line) <= 0)
            {
                stop = true;
                break;
            }
            if(line == "\n") 
            {
                _http_req._blank = line;
                break;
            }
            line.resize(line.size()-1);     // 去掉\n
            _http_req._request_header.push_back(line);
            LOG(INFO, line);
        }
        return true;
    }

    void ParseHttpRequestLine()
    {
        // GET / HTTP/1.1
        auto& line = _http_req._request_line;
        std::stringstream ss(line);
        ss >> _http_req._method >> _http_req._uri >> _http_req._version;
        auto& method = _http_req._method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
    }
    void ParseHttpRequestHeader()
    {
        std::string key;
        std::string value;
        for(auto& line : _http_req._request_header)
        {
            if(Util::CutString(line, key, value, SEP))
            {
                _http_req._header_kv[key] = value;
            }
        }
    }

    bool IsNeedwRecvHttpRequestBody()
    {
        auto method = _http_req._method;
        if(method == "POST")
        {
            // 1. 先获取Content-Length
            // 2. 再根据Content-Length读取请求体
            auto& header_kv = _http_req._header_kv;
            auto it = header_kv.find("Content-Length");
            if(it!= header_kv.end())
            {
                LOG(INFO, "POST Method, Content-Length: " + it->second);
                _http_req.content_length = std::stoi(it->second);
                return true;
            }
        }
        return false;
    }

    bool RecvHttpRequestBody()
    {
        if(IsNeedwRecvHttpRequestBody())
        {
            int content_length = _http_req.content_length;
            auto& body = _http_req._request_body;
            char ch = 0;
            while(content_length)
            {
                ssize_t s = recv(_sock, &ch, 1, 0);
                if(s > 0)
                {
                    body.push_back(ch);
                    content_length--;
                }
                else
                {
                    stop = true;
                    break;
                }
            }
            LOG(INFO, "Recv Request Body: " + body);
        }
        return stop;
    }

    int ProcessCGI()
    {
        LOG(INFO, "process cgi!");

        int code = OK;
        // 父进程数据
        auto& method = _http_req._method;   
        auto& body_test = _http_req._request_body;      // POST
        auto& query_string = _http_req._query_string;   // GET
        auto& bin = _http_req._path;    // 要让子进程执行的文件，一定存在
        int content_length = _http_req.content_length; // POST
        auto& response_body = _http_resp._response_body;

        // 环境变量
        std::string query_string_env;   
        std::string method_env;   
        std::string content_length_env;   

        // 站在父进程角度
        int input[2];   // 父进程读，子进程写
        int output[2];  // 子进程读，父进程写
        if(pipe(input) < 0 || pipe(output) < 0)
        {
            LOG(ERROR, "pipe error!");
            code = SERVER_ERROR;
            return code;
        }

        //  新线程，但是从头到尾都只有一个进程，就是httpserver
        pid_t pid = fork();
        if(pid == 0)
        {
            // 子进程
            close(input[0]);
            close(output[1]);

            // 子进程需要设置环境变量
            method_env = "METHOD=" + method;
            putenv((char*)method_env.c_str());

            if(method == "GET")
            {
                query_string_env = "QUERY_STRING=" + query_string;
                putenv((char*)query_string_env.c_str());
                LOG(INFO, "Get Method, Add Query-String Env");
            }
            else if(method == "POST")
            {
                content_length_env = "CONTENT_LENGTH=" + std::to_string(content_length);
                putenv((char*)content_length_env.c_str());
                LOG(INFO, "Post Method, Add Content-Length Env");
            }
            else
            {
                // Do nothing, 保证代码完整性
            }

            std::cout << "bin: " << bin << std::endl;

            // 站在子进程角度重定向
            // input[1]: 写出 -> 1 -> input[1]
            // output[0]: 读入 -> 0 -> output[0]
            dup2(output[0], 0);
            dup2(input[1], 1);

            // 替换成功之后，目标子进程如何得知对应的读写文件描述符是多少？不需要，只要读0写1即可
            execl(bin.c_str(), bin.c_str(), nullptr);
            exit(1);
        }
        else if(pid < 0)
        {
            // 子进程创建失败
            LOG(ERROR, "fork error!");
            return code;
        }
        else
        {
            // 父进程
            close(input[1]);
            close(output[0]);

            if(method == "POST")
            {
                const char *start = body_test.c_str();
                int total = 0;  // 已经写了多少字节
                int size = 0;   // 本次写入的字节数
                while(total < content_length) {
                    size = write(output[1], start + total, body_test.size() - total);
                    if(size <= 0) break;
                    total += size;
                }
            }

            char ch = 0;
            while(read(input[0], &ch, 1) > 0)
            {
                response_body.push_back(ch);
            }

            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if(ret == pid)
            {
                if(WIFEXITED(status))
                {
                    if(WEXITSTATUS(status) == 0)
                    {
                        code = OK;
                    }
                    else
                    {
                        code = BAD_REQUEST;
                    }
                }
                else
                {
                    code = SERVER_ERROR;
                }
            }
            
            close(input[0]);
            close(output[1]);
        }
        return code;
    }

    int ProcessNoneCGI()
    {
        _http_resp._fd = open(_http_req._path.c_str(), O_RDONLY); 
        if(_http_resp._fd >= 0)
        {
            return OK;
        }
        return NOT_FOUND; 
    }

    void HandlerError(const std::string& page)
    {
        _http_req.cgi = false;
        // 要给用户返回对应的404页面
        _http_resp._fd = open(page.c_str(), O_RDONLY);
        if(_http_resp._fd > 0)
        {
            struct stat st;
            stat(page.c_str(), &st);
            _http_req._size = st.st_size;

            std::string line = "Content-Type: text/html";
            line += LINE_END;
            _http_resp._response_header.push_back(line);

            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += LINE_END;
            _http_resp._response_header.push_back(line);    
        }
    }

    void BuildOKResponse()
    {
        std::string line = "Content-Type: ";
        line += Suffix2Desc(_http_req._suffix);
        line += LINE_END;
        _http_resp._response_header.push_back(line);

        line = "Content-Length: ";
        if(_http_req.cgi)
        {
            line += std::to_string(_http_resp._response_body.size());
            line += LINE_END;
        }
        else
        {
            line += std::to_string(_http_req._size);
            line += LINE_END;
        }
        _http_resp._response_header.push_back(line);
    }

    void BuildHttpResponseHelper()
    {
        auto& code = _http_resp._status_code;
        // 构建响应行
        auto& status_line = _http_resp._status_line;
        status_line += HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";
        status_line += Code2Desc(code);
        status_line += LINE_END;
        
        // 构建响应正文（可能包括响应头）
        std::string path = WEB_ROOT;  
        switch(code)
        {
            case OK:
                BuildOKResponse();
                break;
            case NOT_FOUND:
                path += "/";
                path += PAGE_NOT_FOUND;
                HandlerError(path);
                break;
            case BAD_REQUEST:
                path += "/";
                path += PAGE_BAD_REQUEST;
                HandlerError(path);
                break;
            case SERVER_ERROR:
                path += "/";
                path += PAGE_SERVER_ERROR;
                HandlerError(path);
            default:
                break;
        }
    }

public:
    Endpoint(int sock):_sock(sock), stop(false)
    {}

    // 是否停止
    // 1. 客户端断开连接
    // 2. 读取请求出错
    // 3. 读取请求成功，但是请求体还没有完全读取完毕
    bool IsStop()
    {
        return stop;
    }

    // 读取并且解析请求
    void RcvHttpRequest()
    {
        if(!RecvHttpRequestLine() && RecvHttpRequestHeader())
        {
            ParseHttpRequestLine();
            ParseHttpRequestHeader();
        }
        RecvHttpRequestBody();
    }

    // 构建响应
    void BuildHttpResponse()
    {
        // 请求已经全部读完，接下来要根据请求构建响应
        std::string path;
        struct stat st;
        size_t found = 0;

        auto& code = _http_resp._status_code;
        if(_http_req._method != "GET" && _http_req._method != "POST")
        {
            // 非法请求
            LOG(WARNING, "Invalid method!");
            code = BAD_REQUEST;
            goto END;
        }
        if(_http_req._method == "GET")
        {
            size_t pos = _http_req._uri.find("?");
            if(pos != std::string::npos)
            {
                _http_req._path = _http_req._uri.substr(0, pos);
                _http_req._query_string = _http_req._uri.substr(pos+1);
                _http_req.cgi = true;
            }
            else
            {
                _http_req._path = _http_req._uri;
            }
        }
        else if(_http_req._method == "POST")
        {
            _http_req._path = _http_req._uri;
            _http_req.cgi = true;
        }
        else
        {
            // Do nothing
        }

        path = _http_req._path;
        _http_req._path = WEB_ROOT;
        _http_req._path += path;
        if(_http_req._path[_http_req._path.size()-1] == '/')
        {
            _http_req._path += HOME_PAGE;   // 访问的是首页，默认访问index.html
        }
        if(stat(_http_req._path.c_str(), &st) == 0)
        {
            // 资源存在
            if(S_ISDIR(st.st_mode))
            {
                // 说明请求的资源是一个目录，不被允许，需要做相关处理
                // 虽然是一个目录，但绝对不会以/结尾
                _http_req._path += "/";
                _http_req._path += HOME_PAGE;
                stat(_http_req._path.c_str(), &st);
            }
            if((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
                // 说明资源是可执行文件
                // ... 使用CGI机制编写
                _http_req.cgi = true;
            }
            _http_req._size = st.st_size;
        }
        else
        {
            // 资源不存在
            LOG(WARNING, _http_req._path + " Not Found!");
            code = NOT_FOUND;
            goto END;
        }

        // 处理后缀
        found = _http_req._path.rfind('.');
        if(found == std::string::npos)
        {
            _http_req._suffix = ".html";
        }
        else
        {
            _http_req._suffix = _http_req._path.substr(found);
        }

        if(_http_req.cgi)
        {
            code = ProcessCGI();    // 执行目标程序，拿到结果：_http_resp._response_body
        }
        else
        {
            // 1.目标网页一定是存在的
            // 2.返回并不是单单返回网页，而是要构建HTTP响应
            code = ProcessNoneCGI();   // 简单的网页返回，返回静态网页，只需要打开即可
        }
END:
        BuildHttpResponseHelper();      
    }

    // 发送响应
    void SendHttpResponse()
    {
        send(_sock, _http_resp._status_line.c_str(), _http_resp._status_line.size(), 0);
        for(auto& line : _http_resp._response_header)
        {
            send(_sock, line.c_str(), line.size(), 0);
        }
        send(_sock, _http_resp._blank.c_str(), _http_resp._blank.size(), 0);
        if(_http_req.cgi)
        {
            auto& response_body = _http_resp._response_body;
            ssize_t size = 0;
            size_t total = 0;
            const char* start = response_body.c_str();
            while(total < response_body.size())
            {
                size = send(_sock, start + total, response_body.size() - total, 0);
                if(size <= 0) break;
                total += static_cast<size_t>(size);
            }
        }
        else
        {
            sendfile(_sock, _http_resp._fd, nullptr, _http_req._size);
            close(_http_resp._fd);
        }
    }

    ~Endpoint()
    {
        close(_sock);
    }
};

// #define DEBUG 1 

class CallBack
{
public:
    CallBack(){}

    void operator()(int _sock)
    {
        HandlerRequest(_sock);
    }

    void HandlerRequest(int _sock)   
    {
        LOG(INFO, "Hander Request Begin!");
        int sock = _sock;
        // std::cout << "get a new link...: " << sock << std::endl;
#ifdef DEBUG
        // For test
        char buffer[4096];
        recv(sock, buffer, sizeof(buffer) - 1, 0);      // 接收客户端发送的数据
        std::cout << "----------begin----------" << std::endl;
        std::cout << "recv data: " << buffer << std::endl;
        std::cout << "-----------end-----------" << std::endl;    
#else
        Endpoint *ep = new Endpoint(sock);
        ep->RcvHttpRequest();
        if(!ep->IsStop())
        {
            LOG(INFO, "Recv No Error, Begin Build And Send HttpResponse!");
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        } 
        else
        {
            LOG(WARNING, "Recv Error, Stop Build And Send HttpResponse!");
        }
        delete ep;
#endif
        LOG(INFO, "Hander Request End!");
    } 

    ~CallBack(){}
};

#endif